﻿using System.Collections.Generic;
using System.Reflection.Emit;
using Cil = Mono.Cecil.Cil;

namespace NJection.LambdaConverter.Mappers
{
    internal static class OpCodesMapper
    {
        private static readonly Dictionary<string, OpCode> _opCodes = new Dictionary<string, OpCode>();

        static OpCodesMapper() {
            _opCodes.Add("Nop", OpCodes.Nop);
            _opCodes.Add("Break", OpCodes.Break);
            _opCodes.Add("Ldarg_0", OpCodes.Ldarg_0);
            _opCodes.Add("Ldarg_1", OpCodes.Ldarg_1);
            _opCodes.Add("Ldarg_2", OpCodes.Ldarg_2);
            _opCodes.Add("Ldarg_3", OpCodes.Ldarg_3);
            _opCodes.Add("Ldloc_0", OpCodes.Ldloc_0);
            _opCodes.Add("Ldloc_1", OpCodes.Ldloc_1);
            _opCodes.Add("Ldloc_2", OpCodes.Ldloc_2);
            _opCodes.Add("Ldloc_3", OpCodes.Ldloc_3);
            _opCodes.Add("Stloc_0", OpCodes.Stloc_0);
            _opCodes.Add("Stloc_1", OpCodes.Stloc_1);
            _opCodes.Add("Stloc_2", OpCodes.Stloc_2);
            _opCodes.Add("Stloc_3", OpCodes.Stloc_3);
            _opCodes.Add("Ldarg_S", OpCodes.Ldarg_S);
            _opCodes.Add("Ldarga_S", OpCodes.Ldarga_S);
            _opCodes.Add("Starg_S", OpCodes.Starg_S);
            _opCodes.Add("Ldloc_S", OpCodes.Ldloc_S);
            _opCodes.Add("Ldloca_S", OpCodes.Ldloca_S);
            _opCodes.Add("Stloc_S", OpCodes.Stloc_S);
            _opCodes.Add("Ldnull", OpCodes.Ldnull);
            _opCodes.Add("Ldc_I4_M1", OpCodes.Ldc_I4_M1);
            _opCodes.Add("Ldc_I4_0", OpCodes.Ldc_I4_0);
            _opCodes.Add("Ldc_I4_1", OpCodes.Ldc_I4_1);
            _opCodes.Add("Ldc_I4_2", OpCodes.Ldc_I4_2);
            _opCodes.Add("Ldc_I4_3", OpCodes.Ldc_I4_3);
            _opCodes.Add("Ldc_I4_4", OpCodes.Ldc_I4_4);
            _opCodes.Add("Ldc_I4_5", OpCodes.Ldc_I4_5);
            _opCodes.Add("Ldc_I4_6", OpCodes.Ldc_I4_6);
            _opCodes.Add("Ldc_I4_7", OpCodes.Ldc_I4_7);
            _opCodes.Add("Ldc_I4_8", OpCodes.Ldc_I4_8);
            _opCodes.Add("Ldc_I4_S", OpCodes.Ldc_I4_S);
            _opCodes.Add("Ldc_I4", OpCodes.Ldc_I4);
            _opCodes.Add("Ldc_I8", OpCodes.Ldc_I8);
            _opCodes.Add("Ldc_R4", OpCodes.Ldc_R4);
            _opCodes.Add("Ldc_R8", OpCodes.Ldc_R8);
            _opCodes.Add("Dup", OpCodes.Dup);
            _opCodes.Add("Pop", OpCodes.Pop);
            _opCodes.Add("Jmp", OpCodes.Jmp);
            _opCodes.Add("Call", OpCodes.Call);
            _opCodes.Add("Calli", OpCodes.Calli);
            _opCodes.Add("Ret", OpCodes.Ret);
            _opCodes.Add("Br_S", OpCodes.Br_S);
            _opCodes.Add("Brfalse_S", OpCodes.Brfalse_S);
            _opCodes.Add("Brtrue_S", OpCodes.Brtrue_S);
            _opCodes.Add("Beq_S", OpCodes.Beq_S);
            _opCodes.Add("Bge_S", OpCodes.Bge_S);
            _opCodes.Add("Bgt_S", OpCodes.Bgt_S);
            _opCodes.Add("Ble_S", OpCodes.Ble_S);
            _opCodes.Add("Blt_S", OpCodes.Blt_S);
            _opCodes.Add("Bne_Un_S", OpCodes.Bne_Un_S);
            _opCodes.Add("Bge_Un_S", OpCodes.Bge_Un_S);
            _opCodes.Add("Bgt_Un_S", OpCodes.Bgt_Un_S);
            _opCodes.Add("Ble_Un_S", OpCodes.Ble_Un_S);
            _opCodes.Add("Blt_Un_S", OpCodes.Blt_Un_S);
            _opCodes.Add("Br", OpCodes.Br);
            _opCodes.Add("Brfalse", OpCodes.Brfalse);
            _opCodes.Add("Brtrue", OpCodes.Brtrue);
            _opCodes.Add("Beq", OpCodes.Beq);
            _opCodes.Add("Bge", OpCodes.Bge);
            _opCodes.Add("Bgt", OpCodes.Bgt);
            _opCodes.Add("Ble", OpCodes.Ble);
            _opCodes.Add("Blt", OpCodes.Blt);
            _opCodes.Add("Bne_Un", OpCodes.Bne_Un);
            _opCodes.Add("Bge_Un", OpCodes.Bge_Un);
            _opCodes.Add("Bgt_Un", OpCodes.Bgt_Un);
            _opCodes.Add("Ble_Un", OpCodes.Ble_Un);
            _opCodes.Add("Blt_Un", OpCodes.Blt_Un);
            _opCodes.Add("Switch", OpCodes.Switch);
            _opCodes.Add("Ldind_I1", OpCodes.Ldind_I1);
            _opCodes.Add("Ldind_U1", OpCodes.Ldind_U1);
            _opCodes.Add("Ldind_I2", OpCodes.Ldind_I2);
            _opCodes.Add("Ldind_U2", OpCodes.Ldind_U2);
            _opCodes.Add("Ldind_I4", OpCodes.Ldind_I4);
            _opCodes.Add("Ldind_U4", OpCodes.Ldind_U4);
            _opCodes.Add("Ldind_I8", OpCodes.Ldind_I8);
            _opCodes.Add("Ldind_I", OpCodes.Ldind_I);
            _opCodes.Add("Ldind_R4", OpCodes.Ldind_R4);
            _opCodes.Add("Ldind_R8", OpCodes.Ldind_R8);
            _opCodes.Add("Ldind_Ref", OpCodes.Ldind_Ref);
            _opCodes.Add("Stind_Ref", OpCodes.Stind_Ref);
            _opCodes.Add("Stind_I1", OpCodes.Stind_I1);
            _opCodes.Add("Stind_I2", OpCodes.Stind_I2);
            _opCodes.Add("Stind_I4", OpCodes.Stind_I4);
            _opCodes.Add("Stind_I8", OpCodes.Stind_I8);
            _opCodes.Add("Stind_R4", OpCodes.Stind_R4);
            _opCodes.Add("Stind_R8", OpCodes.Stind_R8);
            _opCodes.Add("Add", OpCodes.Add);
            _opCodes.Add("Sub", OpCodes.Sub);
            _opCodes.Add("Mul", OpCodes.Mul);
            _opCodes.Add("Div", OpCodes.Div);
            _opCodes.Add("Div_Un", OpCodes.Div_Un);
            _opCodes.Add("Rem", OpCodes.Rem);
            _opCodes.Add("Rem_Un", OpCodes.Rem_Un);
            _opCodes.Add("And", OpCodes.And);
            _opCodes.Add("Or", OpCodes.Or);
            _opCodes.Add("Xor", OpCodes.Xor);
            _opCodes.Add("Shl", OpCodes.Shl);
            _opCodes.Add("Shr", OpCodes.Shr);
            _opCodes.Add("Shr_Un", OpCodes.Shr_Un);
            _opCodes.Add("Neg", OpCodes.Neg);
            _opCodes.Add("Not", OpCodes.Not);
            _opCodes.Add("Conv_I1", OpCodes.Conv_I1);
            _opCodes.Add("Conv_I2", OpCodes.Conv_I2);
            _opCodes.Add("Conv_I4", OpCodes.Conv_I4);
            _opCodes.Add("Conv_I8", OpCodes.Conv_I8);
            _opCodes.Add("Conv_R4", OpCodes.Conv_R4);
            _opCodes.Add("Conv_R8", OpCodes.Conv_R8);
            _opCodes.Add("Conv_U4", OpCodes.Conv_U4);
            _opCodes.Add("Conv_U8", OpCodes.Conv_U8);
            _opCodes.Add("Callvirt", OpCodes.Callvirt);
            _opCodes.Add("Cpobj", OpCodes.Cpobj);
            _opCodes.Add("Ldobj", OpCodes.Ldobj);
            _opCodes.Add("Ldstr", OpCodes.Ldstr);
            _opCodes.Add("Newobj", OpCodes.Newobj);
            _opCodes.Add("Castclass", OpCodes.Castclass);
            _opCodes.Add("Isinst", OpCodes.Isinst);
            _opCodes.Add("Conv_R_Un", OpCodes.Conv_R_Un);
            _opCodes.Add("Unbox", OpCodes.Unbox);
            _opCodes.Add("Throw", OpCodes.Throw);
            _opCodes.Add("Ldfld", OpCodes.Ldfld);
            _opCodes.Add("Ldflda", OpCodes.Ldflda);
            _opCodes.Add("Stfld", OpCodes.Stfld);
            _opCodes.Add("Ldsfld", OpCodes.Ldsfld);
            _opCodes.Add("Ldsflda", OpCodes.Ldsflda);
            _opCodes.Add("Stsfld", OpCodes.Stsfld);
            _opCodes.Add("Stobj", OpCodes.Stobj);
            _opCodes.Add("Conv_Ovf_I1_Un", OpCodes.Conv_Ovf_I1_Un);
            _opCodes.Add("Conv_Ovf_I2_Un", OpCodes.Conv_Ovf_I2_Un);
            _opCodes.Add("Conv_Ovf_I4_Un", OpCodes.Conv_Ovf_I4_Un);
            _opCodes.Add("Conv_Ovf_I8_Un", OpCodes.Conv_Ovf_I8_Un);
            _opCodes.Add("Conv_Ovf_U1_Un", OpCodes.Conv_Ovf_U1_Un);
            _opCodes.Add("Conv_Ovf_U2_Un", OpCodes.Conv_Ovf_U2_Un);
            _opCodes.Add("Conv_Ovf_U4_Un", OpCodes.Conv_Ovf_U4_Un);
            _opCodes.Add("Conv_Ovf_U8_Un", OpCodes.Conv_Ovf_U8_Un);
            _opCodes.Add("Conv_Ovf_I_Un", OpCodes.Conv_Ovf_I_Un);
            _opCodes.Add("Conv_Ovf_U_Un", OpCodes.Conv_Ovf_U_Un);
            _opCodes.Add("Box", OpCodes.Box);
            _opCodes.Add("Newarr", OpCodes.Newarr);
            _opCodes.Add("Ldlen", OpCodes.Ldlen);
            _opCodes.Add("Ldelema", OpCodes.Ldelema);
            _opCodes.Add("Ldelem_I1", OpCodes.Ldelem_I1);
            _opCodes.Add("Ldelem_U1", OpCodes.Ldelem_U1);
            _opCodes.Add("Ldelem_I2", OpCodes.Ldelem_I2);
            _opCodes.Add("Ldelem_U2", OpCodes.Ldelem_U2);
            _opCodes.Add("Ldelem_I4", OpCodes.Ldelem_I4);
            _opCodes.Add("Ldelem_U4", OpCodes.Ldelem_U4);
            _opCodes.Add("Ldelem_I8", OpCodes.Ldelem_I8);
            _opCodes.Add("Ldelem_I", OpCodes.Ldelem_I);
            _opCodes.Add("Ldelem_R4", OpCodes.Ldelem_R4);
            _opCodes.Add("Ldelem_R8", OpCodes.Ldelem_R8);
            _opCodes.Add("Ldelem_Ref", OpCodes.Ldelem_Ref);
            _opCodes.Add("Stelem_I", OpCodes.Stelem_I);
            _opCodes.Add("Stelem_I1", OpCodes.Stelem_I1);
            _opCodes.Add("Stelem_I2", OpCodes.Stelem_I2);
            _opCodes.Add("Stelem_I4", OpCodes.Stelem_I4);
            _opCodes.Add("Stelem_I8", OpCodes.Stelem_I8);
            _opCodes.Add("Stelem_R4", OpCodes.Stelem_R4);
            _opCodes.Add("Stelem_R8", OpCodes.Stelem_R8);
            _opCodes.Add("Stelem_Ref", OpCodes.Stelem_Ref);
            _opCodes.Add("Ldelem", OpCodes.Ldelem);
            _opCodes.Add("Stelem", OpCodes.Stelem);
            _opCodes.Add("Unbox_Any", OpCodes.Unbox_Any);
            _opCodes.Add("Conv_Ovf_I1", OpCodes.Conv_Ovf_I1);
            _opCodes.Add("Conv_Ovf_U1", OpCodes.Conv_Ovf_U1);
            _opCodes.Add("Conv_Ovf_I2", OpCodes.Conv_Ovf_I2);
            _opCodes.Add("Conv_Ovf_U2", OpCodes.Conv_Ovf_U2);
            _opCodes.Add("Conv_Ovf_I4", OpCodes.Conv_Ovf_I4);
            _opCodes.Add("Conv_Ovf_U4", OpCodes.Conv_Ovf_U4);
            _opCodes.Add("Conv_Ovf_I8", OpCodes.Conv_Ovf_I8);
            _opCodes.Add("Conv_Ovf_U8", OpCodes.Conv_Ovf_U8);
            _opCodes.Add("Refanyval", OpCodes.Refanyval);
            _opCodes.Add("Ckfinite", OpCodes.Ckfinite);
            _opCodes.Add("Mkrefany", OpCodes.Mkrefany);
            _opCodes.Add("Ldtoken", OpCodes.Ldtoken);
            _opCodes.Add("Conv_U2", OpCodes.Conv_U2);
            _opCodes.Add("Conv_U1", OpCodes.Conv_U1);
            _opCodes.Add("Conv_I", OpCodes.Conv_I);
            _opCodes.Add("Conv_Ovf_I", OpCodes.Conv_Ovf_I);
            _opCodes.Add("Conv_Ovf_U", OpCodes.Conv_Ovf_U);
            _opCodes.Add("Add_Ovf", OpCodes.Add_Ovf);
            _opCodes.Add("Add_Ovf_Un", OpCodes.Add_Ovf_Un);
            _opCodes.Add("Mul_Ovf", OpCodes.Mul_Ovf);
            _opCodes.Add("Mul_Ovf_Un", OpCodes.Mul_Ovf_Un);
            _opCodes.Add("Sub_Ovf", OpCodes.Sub_Ovf);
            _opCodes.Add("Sub_Ovf_Un", OpCodes.Sub_Ovf_Un);
            _opCodes.Add("Endfinally", OpCodes.Endfinally);
            _opCodes.Add("Leave", OpCodes.Leave);
            _opCodes.Add("Leave_S", OpCodes.Leave_S);
            _opCodes.Add("Stind_I", OpCodes.Stind_I);
            _opCodes.Add("Conv_U", OpCodes.Conv_U);
            _opCodes.Add("Prefix7", OpCodes.Prefix7);
            _opCodes.Add("Prefix6", OpCodes.Prefix6);
            _opCodes.Add("Prefix5", OpCodes.Prefix5);
            _opCodes.Add("Prefix4", OpCodes.Prefix4);
            _opCodes.Add("Prefix3", OpCodes.Prefix3);
            _opCodes.Add("Prefix2", OpCodes.Prefix2);
            _opCodes.Add("Prefix1", OpCodes.Prefix1);
            _opCodes.Add("Prefixref", OpCodes.Prefixref);
            _opCodes.Add("Arglist", OpCodes.Arglist);
            _opCodes.Add("Ceq", OpCodes.Ceq);
            _opCodes.Add("Cgt", OpCodes.Cgt);
            _opCodes.Add("Cgt_Un", OpCodes.Cgt_Un);
            _opCodes.Add("Clt", OpCodes.Clt);
            _opCodes.Add("Clt_Un", OpCodes.Clt_Un);
            _opCodes.Add("Ldftn", OpCodes.Ldftn);
            _opCodes.Add("Ldvirtftn", OpCodes.Ldvirtftn);
            _opCodes.Add("Ldarg", OpCodes.Ldarg);
            _opCodes.Add("Ldarga", OpCodes.Ldarga);
            _opCodes.Add("Starg", OpCodes.Starg);
            _opCodes.Add("Ldloc", OpCodes.Ldloc);
            _opCodes.Add("Ldloca", OpCodes.Ldloca);
            _opCodes.Add("Stloc", OpCodes.Stloc);
            _opCodes.Add("Localloc", OpCodes.Localloc);
            _opCodes.Add("Endfilter", OpCodes.Endfilter);
            _opCodes.Add("Unaligned", OpCodes.Unaligned);
            _opCodes.Add("Volatile", OpCodes.Volatile);
            _opCodes.Add("Tailcall", OpCodes.Tailcall);
            _opCodes.Add("Initobj", OpCodes.Initobj);
            _opCodes.Add("Constrained", OpCodes.Constrained);
            _opCodes.Add("Cpblk", OpCodes.Cpblk);
            _opCodes.Add("Initblk", OpCodes.Initblk);
            _opCodes.Add("Rethrow", OpCodes.Rethrow);
            _opCodes.Add("Sizeof", OpCodes.Sizeof);
            _opCodes.Add("Refanytype", OpCodes.Refanytype);
            _opCodes.Add("Readonly", OpCodes.Readonly);
        }

        internal static OpCode Map(Cil.OpCode cecilOpCode) {
            OpCode opCode;

            if (!_opCodes.TryGetValue(cecilOpCode.Code.ToString(), out opCode)) {
                opCode = OpCodes.Nop;
            }

            return opCode;
        }
    }
}